import { game } from "cc";
import { PrintComponent } from "./PrintComponent";

/**
 * FPS计数器的抽象基类
 */
abstract class FPSCounterBase {
    protected m_LastFrameCount: number = 0; // 记录上一帧的帧计数

    /**
     * 尝试进行帧计数
     * @returns 如果计数成功返回true，否则返回false
     */
    public tryCount(): boolean {
        // 确保同一帧只计数一次，且时间增量大于0(避免暂停时统计)
        if (game.totalTime > this.m_LastFrameCount && game.deltaTime > 0) {
            this.m_LastFrameCount = game.totalTime; // 更新帧计数
            this.count(); // 调用具体计数实现
            return true;
        }
        return false;
    }

    /**
     * 重置计数器，开始新的统计
     */
    public abstract startNew(): void;

    /**
     * 具体的计数逻辑，由子类实现
     */
    protected abstract count(): void;
}

/**
 * 基础FPS计数器，提供当前FPS和平均FPS统计
 */
class FPSCounter extends FPSCounterBase {
    private _sampleTimes: number = 0; // 统计样本数
    private _framesInSecond: number = 0; // 当前秒内的帧数
    private _totalTime: number = 0; // 累计时间(秒)
    private _accFPS: number = 0; // 累计FPS值(用于计算平均值)
    private _curFPS: number = 0; // 当前FPS值

    /**
     * 获取当前FPS值
     */
    public get currFPS(): number { return this._curFPS; }

    /**
     * 获取平均FPS值(总FPS/样本数)
     */
    public get avgFPS(): number {
        return this._sampleTimes > 0 ? this._accFPS / this._sampleTimes : 0;
    }

    /**
     * 重置所有统计，开始新的计数
     */
    public override startNew(): void {
        this._sampleTimes = 0;
        this._framesInSecond = 0;
        this._totalTime = 0;
        this._accFPS = 0;
        this._curFPS = 0;
    }

    /**
     * 帧计数实现，每0.5秒计算一次当前FPS
     */
    protected override count(): void {
        this._framesInSecond++; // 增加帧数计数
        this._totalTime += game.deltaTime; // 累加时间

        // 每0.5秒计算一次当前FPS(避免瞬时波动)
        if (this._totalTime >= 0.5) {
            // 计算当前FPS = 帧数/时间
            this._curFPS = this._framesInSecond / this._totalTime;
            this._totalTime = 0; // 重置计时
            this._framesInSecond = 0; // 重置帧数计数
            this._sampleTimes++; // 增加样本数
            this._accFPS += this._curFPS; // 累加FPS值(用于计算平均值)
        }
    }
}

/**
 * FPS百分位计数器，用于分析帧率分布情况
 */
class FPSPercentileCounter extends FPSCounterBase {
    private m_FrameRange: number; // 帧率范围上限
    private m_FrameCounts: number[]; // 帧率分布统计数组
    private m_TotalFrameCount: number = 0; // 总帧数

    /**
     * 构造函数，初始化帧率统计范围
     * @param frameRange 最大帧率值
     */
    constructor(frameRange: number) {
        super();
        this.m_FrameRange = frameRange;
        this.m_FrameCounts = new Array(Math.max(0, frameRange + 1)).fill(0);
    }

    /**
     * 重置统计
     */
    public override startNew(): void {
        this.m_FrameCounts.fill(0);
        this.m_TotalFrameCount = 0;
    }

    /**
     * 帧计数实现，记录当前帧率分布
     */
    protected override count(): void {
        // 计算当前FPS(1/deltaTime)，并限制在m_FrameRange范围内
        const fps = Math.min(Math.round(1.0 / game.deltaTime), this.m_FrameRange);
        this.m_FrameCounts[fps]++; // 对应FPS计数+1
        this.m_TotalFrameCount++; // 总帧数+1
    }

    /**
     * 获取指定百分位的FPS值
     * @param percentile 百分位值(0-100)
     * @returns 对应百分位的FPS值
     */
    public getFramePercentile(percentile: number): number {
        // 计算目标百分位对应的帧数位置
        const mark = this.m_TotalFrameCount * Math.min(Math.max(percentile / 100, 0), 1);

        let count = 0; // 累计帧数计数器

        // 从低FPS到高FPS遍历，找到达到百分位的位置
        for (let i = 0; i < this.m_FrameCounts.length; i++) {
            count += this.m_FrameCounts[i];

            // 当累计帧数超过目标位置时，返回当前FPS值
            if (count >= mark) {
                return i;
            }
        }

        return this.m_FrameRange; // 默认返回最大FPS值
    }
}

/**
 * 卡顿计数器，检测和统计游戏卡顿情况
 */
class FPSJankCounter extends FPSCounterBase {
    private static readonly FilmFrameTime1: number = 1 / 30;  // ≈41.67ms (电影标准帧时间)
    private static readonly FilmFrameTime2: number = FPSJankCounter.FilmFrameTime1 * 2; // ≈83.33ms
    private static readonly FilmFrameTime3: number = FPSJankCounter.FilmFrameTime1 * 3; // ≈125ms

    private static readonly FrameTimeLength: number = 3; // 帧时间历史记录数组长度

    private m_FrameTimes: number[] = new Array(FPSJankCounter.FrameTimeLength).fill(0); // 最近几帧的时间记录
    private m_FrameTimeIndex: number = 0; // 当前帧时间索引

    private _startTime: number = 0; // 统计开始时间
    private _smallJankCount: number = 0; // 轻微卡顿次数
    private _bigJankCount: number = 0; // 严重卡顿次数
    private _smallJankTime: number = 0; // 轻微卡顿总时间
    private _bigJankTime: number = 0; // 严重卡顿总时间

    /**
     * 获取统计开始时间
     */
    public get startTime(): number { return this._startTime; }

    /**
     * 获取轻微卡顿次数
     */
    public get smallJankCount(): number { return this._smallJankCount; }

    /**
     * 获取严重卡顿次数
     */
    public get bigJankCount(): number { return this._bigJankCount; }

    /**
     * 获取轻微卡顿总时间
     */
    public get smallJankTime(): number { return this._smallJankTime; }

    /**
     * 获取严重卡顿总时间
     */
    public get bigJankTime(): number { return this._bigJankTime; }

    /**
     * 重置所有统计
     */
    public override startNew(): void {
        this._smallJankCount = 0;
        this._bigJankCount = 0;
        this._smallJankTime = 0;
        this._bigJankTime = 0;
        this.m_FrameTimes.fill(0);
        this.m_FrameTimeIndex = 0;
        this._startTime = game.totalTime; // 记录开始时间
    }

    /**
     * 卡顿检测逻辑
     * 卡顿定义:
     * 1. 当前帧时间 > 前三帧平均时间的2倍
     * 2. 并且超过特定阈值:
     *    - 轻微卡顿: >2电影帧时间(≈83.33ms)
     *    - 严重卡顿: >3电影帧时间(≈125ms)
     */
    protected override count(): void {
        const time = game.deltaTime; // 获取当前帧时间

        // 记录当前帧时间到历史数组
        this.m_FrameTimes[this.m_FrameTimeIndex++ % FPSJankCounter.FrameTimeLength] = time;

        // 当有足够历史数据时(3帧)，开始卡顿检测
        if (this.m_FrameTimeIndex >= FPSJankCounter.FrameTimeLength) {
            let total = 0;

            // 计算前三帧总时间
            for (let i = 0; i < FPSJankCounter.FrameTimeLength; i++) {
                total += this.m_FrameTimes[i];
            }

            const average = total / FPSJankCounter.FrameTimeLength; // 计算平均帧时间

            // 卡顿条件1: 当前帧时间 > 平均帧时间的2倍
            if (time > average * 2) {
                // 严重卡顿条件: >3电影帧时间(≈125ms)
                if (time > FPSJankCounter.FilmFrameTime3) {
                    this._bigJankTime += time; // 累加卡顿时间
                    this._bigJankCount++;      // 增加卡顿计数
                    return;
                }
                // 轻微卡顿条件: >2电影帧时间(≈83.33ms)
                else if (time > FPSJankCounter.FilmFrameTime2) {
                    this._smallJankTime += time;
                    this._smallJankCount++;
                    return;
                }
            }
        }
    }

    /**
     * 获取统计持续时间
     * @returns 持续时间(秒)
     */
    public getDuration(): number {
        return game.totalTime - this._startTime;
    }
}

export class FPSCountercontroller {
    private fpsCounter: FPSCounter;
    private fpsPercentileCounter: FPSPercentileCounter;
    private fpsJankCounter: FPSJankCounter;

    private delayStutterTime: number = 10;
    private elapsedStutterTime: number = 0;
    private stutterLastTime: number = 0;
    constructor() {
        this.fpsCounter = new FPSCounter();
        this.fpsPercentileCounter = new FPSPercentileCounter(60);
        this.fpsJankCounter = new FPSJankCounter();
        this.stutterLastTime = new Date().getTime();
    }


    public start(): void {
        this.elapsedStutterTime = 0;
        this.fpsCounter.startNew();
        this.fpsPercentileCounter.startNew();
        this.fpsJankCounter.startNew();
    }


    // pd_logic_module_tag：上报场景，当前处于什么场景中（比如 stage - 1、stage - 2、stage - 3表示对应关卡中；interval表示两关之间；none表示其他场景，如开始前、结束后、关闭游戏等）
    // pd_duration：两次打点前台时间间隔 (游戏中每10s上报一次，跳出/关卡结束上报1次。中间选择就不上报了。所以重点是计算特殊节点导致游戏进程被打断或结束的时长)
    // pd_fps：平均帧率，两次打点之间的平均帧率
    // pd_fps_01：帧率1 % 分数位，两次打点之间的1 % 分位数的帧率
    // pd_fps_0_1：帧率0.1 % 分数位，两次打点之间的0.1 % 分位数的帧率
    // pd_fps_10：帧率10 % 分数位，两次打点之间的10 % 分位数的帧率
    // pd_fps_20：帧率20 % 分数位，两次打点之间的20 % 分位数的帧率
    // pd_fps_30：帧率30 % 分数位，两次打点之间的30 % 分位数的帧率
    // pd_fps_50：帧率50 % 分数位，两次打点之间的50 % 分位数的帧率
    // pd_big_jank_count：大卡顿数，两次打点之间，满足条件的帧数。条件：当前帧时间 > 平均帧时间的2倍 且 当前帧时间 > 3电影帧时间(≈125ms)
    // pd_big_jank_time：大卡顿时间：大卡顿时间，两次打点之间，满足条件的累计帧时间。条件：当前帧时间 > 平均帧时间的2倍 且 当前帧时间 > 3电影帧时间(≈125ms)
    // pd_small_jank_count：小卡顿数，两次打点之间，满足条件的帧数。条件：当前帧时间 > 平均帧时间的2倍 且 当前帧时间 > 2电影帧时间(≈83.33ms)
    // pd_small_jank_time：小卡顿时间，两次打点之间，满足条件的累计帧时间。条件：当前帧时间 > 平均帧时间的2倍 且 当前帧时间 > 2电影帧时间(≈83.33ms)
    public stutter(dt: number, stage: number, force: boolean): void {
        this.fpsCounter.tryCount();
        this.fpsPercentileCounter.tryCount();
        this.fpsJankCounter.tryCount();

        this.elapsedStutterTime += dt;
        let gameTime = new Date().getTime();
        if (this.elapsedStutterTime >= this.delayStutterTime || force) {
            const pd_duration: number = gameTime - this.stutterLastTime;
            this.stutterLastTime = gameTime;

            const param = {
                pd_logic_module_tag: "stage-" + stage,
                pd_duration: pd_duration,
                pd_fps: Math.round(this.fpsCounter.avgFPS),
                pd_fps_0_1: this.fpsPercentileCounter.getFramePercentile(0.1),
                pd_fps_01: this.fpsPercentileCounter.getFramePercentile(1),
                pd_fps_10: this.fpsPercentileCounter.getFramePercentile(10),
                pd_fps_20: this.fpsPercentileCounter.getFramePercentile(20),
                pd_fps_30: this.fpsPercentileCounter.getFramePercentile(30),
                pd_fps_50: this.fpsPercentileCounter.getFramePercentile(50),
                pd_big_jank_count: this.fpsJankCounter.bigJankCount,
                pd_big_jank_time: this.fpsJankCounter.bigJankTime * 1000,
                pd_small_jank_count: this.fpsJankCounter.smallJankCount,
                pd_small_jank_time: this.fpsJankCounter.smallJankTime * 1000,
            }

            PrintComponent.stutter(param);

            console.log('Print stutter pd_duration: ', pd_duration);

            // 每次上报后重置
            this.start();
        }
    }
}